home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #1 / Ham Radio 2000.iso / ham2000 / tcp_ip / tnos / tnos100s / smtpcli.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-01-15  |  27.2 KB  |  1,167 lines

  1. /*
  2.  *    CLIENT routines for Simple Mail Transfer Protocol ala RFC821
  3.  *    A.D. Barksdale Garbee II, aka Bdale, N3EUA
  4.  *    Copyright 1986 Bdale Garbee, All Rights Reserved.
  5.  *    Permission granted for non-commercial copying and use, provided
  6.  *    this notice is retained.
  7.  *     Modified 14 June 1987 by P. Karn for symbolic target addresses,
  8.  *    also rebuilt locking mechanism
  9.  *    Copyright 1987 1988 David Trulli, All Rights Reserved.
  10.  *    Permission granted for non-commercial copying and use, provided
  11.  *    this notice is retained.
  12.  */
  13. /* Mods by G1EMM and PA0GRI */
  14. #include <stdio.h>
  15. #include <fcntl.h>
  16. #include <time.h>
  17. #include <setjmp.h>
  18. #ifdef UNIX
  19. #include <sys/types.h>
  20. #endif
  21. #ifdef    AMIGA
  22. #include <stat.h>
  23. #else
  24. #include <sys/stat.h>
  25. #endif
  26. #ifdef    __TURBOC__
  27. #include <dir.h>
  28. #include <io.h>
  29. #endif
  30. #include "global.h"
  31. #include "config.h"
  32. #ifdef    ANSIPROTO
  33. #include <stdarg.h>
  34. #endif
  35. #include "mbuf.h"
  36. #include "cmdparse.h"
  37. #include "proc.h"
  38. #include "socket.h"
  39. #ifdef    LZW
  40. #include "lzw.h"
  41. #endif
  42. #include "timer.h"
  43. #include "netuser.h"
  44. #include "smtp.h"
  45. #include "dirutil.h"
  46. #include "commands.h"
  47. #include "session.h"
  48. #include "files.h"
  49.  
  50. struct timer Smtpcli_t;
  51. static int32 Gateway;
  52.  
  53. #ifdef SMTPTRACE
  54. static unsigned short Smtptrace = 0;        /* used for trace level */
  55. static int dosmtptrace __ARGS((int argc,char *argv[],void *p));
  56. static char smtp_recv[] = "smtpcli recv: %s\n";
  57. #endif
  58.  
  59. static unsigned  short Smtpmaxcli  = MAXSESSIONS;    /* the max client connections allowed */
  60. static int Smtpsessions = 0;        /* number of client connections
  61.                     * currently open */
  62. #ifdef    LZW
  63. int Smtpslzw = 1;
  64. int Smtpclzw = 1;
  65. static int Smtpbatch = 1;
  66. #else
  67. static int Smtpbatch = 0;
  68. #endif
  69. int Smtpbidcheck = 1;
  70. int Smtpheaders = 1;
  71. int    Smtpmode = 0;
  72.  
  73. int    Smtpquiet = 0;
  74. int    UseMX = 0;            /* use MX records in domain lookup */
  75.  
  76. static struct smtpcli *cli_session[MAXSESSIONS]; /* queue of client sessions  */
  77.  
  78. static void dosmtptick __ARGS((int i,void *p1,void *p2));
  79. static void del_job __ARGS((struct smtp_job *jp));
  80. static void del_session __ARGS((struct smtpcli *cb));
  81. static int dogateway __ARGS((int argc,char *argv[],void *p));
  82. static int dosmtpmaxcli __ARGS((int argc,char *argv[],void *p));
  83. static int dotimer __ARGS((int argc,char *argv[],void *p));
  84. static int doquiet __ARGS((int argc,char *argv[],void *p));
  85. static int doclzw __ARGS((int argc,char *argv[],void *p));
  86. static int doslzw __ARGS((int argc,char *argv[],void *p));
  87. static int dousemx __ARGS((int argc,char *argv[],void *p));
  88. static int dosmtpkill __ARGS((int argc,char *argv[],void *p));
  89. static int dosmtplist __ARGS((int argc,char *argv[],void *p));
  90. static int dobatch __ARGS((int argc,char *argv[],void *p));
  91. static void execjobs __ARGS((void));
  92. static int getresp __ARGS((struct smtpcli *ftp,int mincode));
  93. static void logerr __ARGS((struct smtpcli *cb,char *line));
  94. static struct smtpcli *lookup __ARGS((int32 destaddr));
  95. static struct smtpcli *newcb __ARGS((void));
  96. static int next_job __ARGS((struct smtpcli *cb));
  97. static void retmail __ARGS((struct smtpcli *cb));
  98. static void sendcmd __ARGS((struct smtpcli *cb,char *fmt,...));
  99. static int smtpsendfile __ARGS((struct smtpcli *cb));
  100. static int setsmtpmode __ARGS((int argc,char *argv[],void *p));
  101. static struct smtp_job *setupjob __ARGS((struct smtpcli *cb,char *id,char *from));
  102. static void smtp_send __ARGS((int unused,void *cb1,void *p));
  103. static int smtpkick __ARGS((int argc,char *argv[],void *p));
  104. static int dosmtpt4 __ARGS((int argc,char *argv[],void *p));
  105. static int dobidcheck __ARGS((int argc,char *argv[],void *p));
  106. static int doheaders __ARGS((int argc,char *argv[],void *p));
  107.  
  108. static struct cmds DFAR Smtpcmds[] = {
  109.     "batch",    dobatch,    0,    0,    NULLCHAR,
  110.     "bidcheck",    dobidcheck,    0,    0,    NULLCHAR,
  111.     "gateway",    dogateway,    0,    0,    NULLCHAR,
  112.     "headers",    doheaders,    0,    0,    NULLCHAR,
  113.     "kick",        smtpkick,    0,    0,    NULLCHAR,
  114.     "kill",        dosmtpkill,    0,    2,    "kill <jobnumber>",
  115.     "list",        dosmtplist,    0,    0,    NULLCHAR,
  116.     "maxclients",    dosmtpmaxcli,    0,    0,    NULLCHAR,
  117.     "mode",        setsmtpmode,    0,    0,    NULLCHAR,
  118.     "quiet",    doquiet,    0,    0,    NULLCHAR,
  119. #ifdef    LZW
  120.     "reclzw",    doslzw,        0,    0,    NULLCHAR,
  121.     "sendlzw",    doclzw,        0,    0,    NULLCHAR,
  122. #endif
  123.     "timer",    dotimer,    0,    0,    NULLCHAR,
  124. #ifdef SMTPTRACE
  125.     "trace",    dosmtptrace,    0,    0,    NULLCHAR,
  126. #endif
  127.     "t4",        dosmtpt4,    0,    0,    NULLCHAR,
  128.     "usemx",    dousemx,    0,    0,    NULLCHAR,
  129.     NULLCHAR,
  130. };
  131.  
  132. int
  133. dosmtp(argc,argv,p)
  134. int argc;
  135. char *argv[];
  136. void *p;
  137. {
  138.     return subcmd(Smtpcmds,argc,argv,p);
  139. }
  140.  
  141. int Smtpt4;
  142.  
  143. static int
  144. dosmtpt4(argc,argv,p)
  145. int argc;
  146. char *argv[];
  147. void *p;
  148. {
  149.     return setint(&Smtpt4,"SMTP T4",argc,argv);
  150. }
  151.  
  152. static int
  153. dobatch(argc,argv,p)
  154. int argc;
  155. char *argv[];
  156. void *p;
  157. {
  158.     return setbool(&Smtpbatch,"SMTP batching",argc,argv);
  159. }
  160.  
  161. static int
  162. doheaders(argc,argv,p)
  163. int argc;
  164. char *argv[];
  165. void *p;
  166. {
  167.     return setbool(&Smtpheaders,"SMTP RFC-822 headers in data for local BBS messages",argc,argv);
  168. }
  169.  
  170. static int
  171. dobidcheck(argc,argv,p)
  172. int argc;
  173. char *argv[];
  174. void *p;
  175. {
  176.     return setbool(&Smtpbidcheck,"SMTP bid checking",argc,argv);
  177. }
  178.  
  179. #ifdef LZW
  180. static int
  181. doclzw(argc,argv,p)
  182. int argc;
  183. char *argv[];
  184. void *p;
  185. {
  186.     return setbool(&Smtpclzw,"SMTP send lzw",argc,argv);
  187. }
  188. #endif
  189.  
  190. static int
  191. doquiet(argc,argv,p)
  192. int argc;
  193. char *argv[];
  194. void *p;
  195. {
  196.     return setbool(&Smtpquiet,"SMTP quiet",argc,argv);
  197. }
  198.  
  199. #ifdef LZW
  200. static int
  201. doslzw(argc,argv,p)
  202. int argc;
  203. char *argv[];
  204. void *p;
  205. {
  206.     return setbool(&Smtpslzw,"SMTP recv lzw",argc,argv);
  207. }
  208. #endif
  209.  
  210. static int
  211. dousemx(argc,argv,p)
  212. int argc;
  213. char *argv[];
  214. void *p;
  215. {
  216.     return setbool(&UseMX,"MX records used",argc,argv);
  217. }
  218. static int
  219. dosmtpmaxcli(argc,argv,p)
  220. int argc;
  221. char *argv[];
  222. void *p;
  223. {
  224.     return setshort(&Smtpmaxcli,"Max clients",argc,argv);
  225. }
  226.  
  227. static int
  228. setsmtpmode(argc,argv,p)
  229. int argc;
  230. char *argv[];
  231. void *p;
  232. {
  233.     if (argc < 2) {
  234.         tprintf("smtp mode: %s\n",
  235.             (Smtpmode & QUEUE) ? "queue" : "route");
  236.     } else {
  237.         switch(*argv[1]) {
  238.         case 'q':
  239.             Smtpmode |= QUEUE;
  240.             break;
  241.         case 'r':
  242.             Smtpmode &= ~QUEUE;
  243.             break;
  244.         default:
  245.             tprintf("Usage: smtp mode [queue | route]\n");
  246.             break;
  247.         }
  248.     }
  249.     return 0;
  250. }
  251. static int
  252. dogateway(argc,argv,p)
  253. int argc;
  254. char *argv[];
  255. void *p;
  256. {
  257.     int32 n;
  258.  
  259.     if(argc < 2){
  260.         tprintf("%s\n",inet_ntoa(Gateway));
  261.     } else if((n = resolve(argv[1])) == 0){
  262.         tprintf(Badhost,argv[1]);
  263.         return 1;
  264.     } else
  265.         Gateway = n;
  266.     return 0;
  267. }
  268.  
  269. #ifdef SMTPTRACE
  270. static int
  271. dosmtptrace(argc,argv,p)
  272. int argc;
  273. char *argv[];
  274. void *p;
  275. {
  276.     return setshort(&Smtptrace,"SMTP tracing",argc,argv);
  277. }
  278. #endif
  279.  
  280. /* list jobs waiting to be sent in the mqueue */
  281. static int
  282. dosmtplist(argc,argv,p)
  283. int argc;
  284. char *argv[];
  285. void *p;
  286. {
  287.     char tstring[80];
  288.     char line[20];
  289.     char host[LINELEN];
  290.     char to[LINELEN];
  291.     char from[LINELEN];
  292.     char *cp;
  293.     char status;
  294.     int flowsave;
  295.     struct stat stbuf;
  296.     struct tm *tminfo, *localtime();
  297.     FILE *fp;
  298.  
  299.     flowsave = Current->flowmode;
  300.     Current->flowmode = 1; /* Enable the more mechanism */
  301. #ifdef    TABS
  302.     tprintf("S\tJob   Size Date  Time  Host\t\tFrom\n");
  303. #else
  304.     tprintf("S       Job   Size Date  Time  Host              From\n");
  305. #endif
  306.     filedir(Mailqueue,0,line);
  307.     while(line[0] != '\0') {
  308.         sprintf(tstring,"%s/%s",Mailqdir,line);
  309.         if ((fp = fopen(tstring,READ_TEXT)) == NULLFILE) {
  310.             tprintf("Can't open %s: %s\n",tstring,sys_errlist[errno]);
  311.             continue;
  312.         }
  313.         if ((cp = strrchr(line,'.')) != NULLCHAR)
  314.             *cp = '\0';
  315.         sprintf(tstring,"%s/%s.lck",Mailqdir,line);
  316.         if (access(tstring,0))
  317.             status = ' ';
  318.         else
  319.             status = 'L';
  320.         sprintf(tstring,"%s/%s.txt",Mailqdir,line);
  321.         stat(tstring,&stbuf);
  322.         tminfo = localtime(&stbuf.st_ctime);
  323.         fgets(host,sizeof(host),fp);
  324.         rip(host);
  325.         fgets(from,sizeof(from),fp);
  326.         rip(from);
  327. #ifdef    TABS
  328.         tprintf("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n\t",
  329. #else
  330.         tprintf("%c %7s %7ld %02d/%02d %02d:%02d %-20s %s\n        ",
  331. #endif
  332.             status, line, stbuf.st_size,
  333.             tminfo->tm_mon+1,
  334.             tminfo->tm_mday,
  335.             tminfo->tm_hour,
  336.             tminfo->tm_min,
  337.             host,from);
  338.         while (fgets(to,sizeof(to),fp) != NULLCHAR) {
  339.             rip(to);
  340.             tprintf("%s ",to);
  341.         }
  342.         tprintf("\n");
  343.         (void) fclose(fp);
  344.         pwait(NULL);
  345.         filedir(Mailqueue,1,line);
  346.     }
  347.     Current->flowmode = flowsave;
  348.     return 0;
  349. }
  350.  
  351. /* kill a job in the mqueue */
  352. static int
  353. dosmtpkill(argc,argv,p)
  354. int argc;
  355. char *argv[];
  356. void *p;
  357. {
  358.     char s[SLINELEN];
  359.     char *cp,c;
  360.     sprintf(s,"%s/%s.lck",Mailqdir,argv[1]);
  361.     cp = strrchr(s,'.');
  362.     if (!access(s,0)) {
  363.         Current->ttystate.echo = Current->ttystate.edit = 0;
  364.         c = keywait("Warning, job is locked by SMTP. Remove (y/n)? ",0);
  365.         Current->ttystate.echo = Current->ttystate.edit = 1;
  366.         if (c != 'y')
  367.             return 0;
  368.         (void) unlink(s);
  369.     }
  370.     strcpy(cp,".wrk");
  371.     if (unlink(s))
  372.         tprintf("Job id %s not found\n",argv[1]);
  373.     strcpy(cp,".txt");
  374.     (void) unlink(s);
  375.     return 0;
  376. }
  377.  
  378. /* Set outbound spool scan interval */
  379. static int
  380. dotimer(argc,argv,p)
  381. int argc;
  382. char *argv[];
  383. void *p;
  384. {
  385.     if(argc < 2){
  386.         tprintf("smtp timer = %lu/%lu\n",
  387.         read_timer(&Smtpcli_t)/1000L,
  388.         dur_timer(&Smtpcli_t)/1000L);
  389.         return 0;
  390.     }
  391.     Smtpcli_t.func = (void (*)())smtptick;/* what to call on timeout */
  392.     Smtpcli_t.arg = NULL;        /* dummy value */
  393.     set_timer(&Smtpcli_t,atol(argv[1])*1000L);    /* set timer duration */
  394.     start_timer(&Smtpcli_t);        /* and fire it up */
  395.     return 0;
  396. }
  397.  
  398. static void
  399. dosmtpkick(i,p1,p2)
  400. int i;
  401. void *p1, *p2;
  402. {
  403.     int32 addr = 0;
  404.     if((int)p2 > 1 && (addr = resolve((char *)p1)) == 0)
  405.         tprintf(Badhost,(char *)p1);
  406.     else
  407.         dosmtptick(0, (void *)addr, (void *)0);
  408. }
  409.  
  410.  
  411. static int
  412. smtpkick(argc,argv,p)
  413. int argc;
  414. char *argv[];
  415. void *p;
  416. {
  417.     newproc("smtp client",1024,dosmtpkick,0,(void *)argv[1],(void *)argc,0);
  418.     return 0;
  419. }
  420.  
  421.  
  422. int
  423. smtptick(t)
  424. void *t;
  425. {
  426.     newproc("smtp client",1024,dosmtptick,0,t,(void *)0,0);
  427. }
  428.  
  429.  
  430. /* This is the routine that gets called every so often to do outgoing
  431.  * mail processing. When called with a null argument, it runs the entire
  432.  * queue; if called with a specific non-zero IP address from the remote
  433.  * kick server, it only starts up sessions to that address.
  434.  */
  435. static void
  436. dosmtptick(t,p1,p2)
  437. int t;
  438. void *p1, *p2;
  439. {
  440.     register struct smtpcli *cb;
  441.     struct smtp_job *jp;
  442.     struct list *ap;
  443.     char    tmpstring[LINELEN], wfilename[13], prefix[9];
  444.     char    from[LINELEN], to[LINELEN];
  445.     char *cp, *cp1;
  446.     int32 destaddr,target;
  447.     FILE *wfile;
  448.     static int Ssmtpcli = 0;
  449.  
  450.     target = (int32)p1;
  451. #ifdef SMTPTRACE
  452.     if (Smtptrace > 5)
  453.         printf("smtp daemon entered, target = %s\n",inet_ntoa(target));
  454. #endif
  455.     if(availmem() < Memthresh){
  456.         /* Memory is tight, don't do anything */
  457.         /* Restart timer */
  458.         start_timer(&Smtpcli_t);
  459.         return;
  460.     }
  461.     /* permit only one general purpose smtp client at a time */
  462.     if (!target)    {
  463.         if (Ssmtpcli)    {
  464.             start_timer(&Smtpcli_t);
  465.             return;
  466.         } else
  467.             Ssmtpcli = 1;
  468.     }
  469.     pwait (NULL);
  470.     for(filedir(Mailqueue,0,wfilename);wfilename[0] != '\0';
  471.         filedir(Mailqueue,1,wfilename)){
  472.  
  473.         /* save the prefix of the file name which it job id */
  474.         cp = wfilename;
  475.         cp1 = prefix;
  476.         while (*cp && *cp != '.')
  477.             *cp1++ = *cp++;
  478.         *cp1 = '\0';
  479.  
  480.         pwait (NULL);
  481.         /* lock this file from the smtp daemon */
  482.         if (mlock(Mailqdir,prefix))
  483.             continue;
  484.  
  485.         pwait (NULL);
  486.         sprintf(tmpstring,"%s/%s",Mailqdir,wfilename);
  487.         if ((wfile = fopen(tmpstring,READ_TEXT)) == NULLFILE) {
  488.             /* probably too many open files */
  489.             (void) rmlock(Mailqdir,prefix);
  490.             /* continue to next message. The failure may be temporary */
  491.             continue;
  492.         }
  493.  
  494.         (void) fgets(tmpstring,LINELEN,wfile);    /* read target host */
  495.         rip(tmpstring);
  496.  
  497.         pwait (NULL);
  498.         if ((destaddr = mailroute(tmpstring)) == 0) {
  499.             fclose(wfile);
  500.             printf("** smtp: Unknown address %s\n",tmpstring);
  501.             (void) rmlock(Mailqdir,prefix);
  502.             continue;
  503.         }
  504.         if(target != 0 && destaddr != target){
  505.             fclose(wfile);
  506.             (void) rmlock(Mailqdir,prefix);
  507.             continue;    /* Not the proper target of a kick */
  508.         }
  509.         pwait (NULL);
  510.         if ((cb = lookup(destaddr)) == NULLSMTPCLI) {
  511.             /* there are enough processes running already */
  512.             if (Smtpsessions >= Smtpmaxcli) {
  513. #ifdef SMTPTRACE
  514.                 if (Smtptrace)
  515.                     printf("smtp daemon: too many processes\n");
  516. #endif
  517.                 log (-1, "smtp daemon: too many processes");
  518.                 fclose(wfile);
  519.                 (void) rmlock(Mailqdir,prefix);
  520.                 break;
  521.             }
  522.             if ((cb = newcb()) == NULLSMTPCLI) {
  523.                 fclose(wfile);
  524.                 (void) rmlock(Mailqdir,prefix);
  525.                 pwait (NULL);
  526.                 break;
  527.             } 
  528.             cb->ipdest = destaddr;
  529.             cb->destname = strdup(tmpstring);
  530.         } else {
  531.             if(cb->lock){
  532.                 /* This system is already is sending mail lets not
  533.                 * interfere with its send queue.
  534.                 */
  535.                 fclose(wfile);
  536.                 (void) rmlock(Mailqdir,prefix);
  537.                 continue;
  538.             }
  539.         }
  540.  
  541.         pwait (NULL);
  542.         (void) fgets(from,LINELEN,wfile);    /* read from */
  543.         rip(from);
  544.         if ((jp = setupjob(cb,prefix,from)) == NULLJOB) {
  545.             fclose(wfile);
  546.             (void) rmlock(Mailqdir,prefix);
  547.             del_session(cb);
  548.             break;
  549.         }
  550.         pwait (NULL);
  551.         while (fgets(to,LINELEN,wfile) != NULLCHAR) {
  552.             rip(to);
  553.             if (addlist(&jp->to,to,DOMAIN,to) == NULLLIST) {
  554.                 fclose(wfile);
  555.                 del_session(cb);
  556.             }
  557.             pwait (NULL);
  558.         }
  559.         fclose(wfile);
  560. #ifdef SMTPTRACE
  561.         if (Smtptrace > 1) {
  562.             printf("queue job %s From: %s To:",prefix,from);
  563.             for (ap = jp->to; ap != NULLLIST; ap = ap->next)
  564.                 printf(" %s",ap->val);
  565.             printf("\n");
  566.         }
  567. #endif
  568.     }
  569.  
  570.     /* start sending that mail */
  571.     execjobs();
  572.     pwait (NULL);
  573.  
  574.     /* Restart timer */
  575.     start_timer(&Smtpcli_t);
  576.     Ssmtpcli = 0;
  577.     return;
  578. }
  579.  
  580. /* This is the master state machine that handles a single SMTP transaction.
  581.  * It is called with a queue of jobs for a particular host.
  582.  * The logic is complicated by the "Smtpbatch" variable, which controls
  583.  * the batching of SMTP commands. If Smtpbatch is true, then many of the
  584.  * SMTP commands are sent in one swell foop before waiting for any of
  585.  * the responses. Unfortunately, this breaks many brain-damaged SMTP servers
  586.  * out there, so provisions have to be made to operate SMTP in lock-step mode.
  587.  */
  588. static void
  589. smtp_send(unused,cb1,p)
  590. int unused;
  591. void *cb1;
  592. void *p;
  593. {
  594.     register struct smtpcli *cb;
  595.     register struct list *tp;
  596.     struct sockaddr_in fsocket;
  597.     char *cp;
  598.     int rcode;
  599.     int rcpts;
  600.     int goodrcpt;
  601.     int i;
  602.     int smtpbatch;
  603.     int init = 1;
  604. #ifdef    LZW
  605.     int lzwmode, lzwbits;
  606.     extern int16 Lzwbits;
  607.     extern int Lzwmode;
  608. #endif
  609.  
  610.     cb = (struct smtpcli *)cb1;
  611.     cb->lock = 1;
  612.     fsocket.sin_family = AF_INET;
  613.     fsocket.sin_addr.s_addr = cb->ipdest;
  614.     fsocket.sin_port = IPPORT_SMTP;
  615.  
  616.     cb->s = socket(AF_INET,SOCK_STREAM,0);
  617.     sockmode(cb->s,SOCK_ASCII);
  618.     setflush(cb->s,-1);    /* We'll explicitly flush before reading */
  619. #ifdef SMTPTRACE
  620.     if (Smtptrace) 
  621.         printf("SMTP client Trying...\n");
  622. #endif
  623.     /* Set a timeout for this connection */
  624.     alarm(Smtpt4 * 1000L);
  625.     if(connect(cb->s,(char *)&fsocket,SOCKSIZE) != 0){
  626.         alarm(0L);
  627.         close_s(cb->s);     /* to make sure it's closed */
  628.         if(Smtpt4 && Gateway && (fsocket.sin_addr.s_addr != Gateway) ) {
  629.             /* Try it via the gateway */
  630.             fsocket.sin_addr.s_addr = Gateway;
  631.             cb->s = socket(AF_INET,SOCK_STREAM,0);
  632.             sockmode(cb->s,SOCK_ASCII);
  633.             setflush(cb->s,-1); /* We'll explicitly flush before reading */
  634. #ifdef SMTPTRACE
  635.             if (Smtptrace)
  636.                 printf("SMTP client Trying gateway...\n");
  637. #endif
  638.             /* Set a timeout for this connection */
  639.             alarm(Smtpt4 * 1000L);
  640.             if(connect(cb->s,(char *)&fsocket,SOCKSIZE) != 0){
  641.                 alarm(0L);
  642.                 cp = sockerr(cb->s);
  643.                 close_s(cb->s);     /* to make sure it's closed */
  644. #ifdef SMTPTRACE
  645.                 if (Smtptrace)
  646.                     printf("Connect failed: %s\n",cp != NULLCHAR ? cp : "");
  647. #endif
  648.                 log(cb->s,"SMTP %s Connect failed: %s",psocket(&fsocket),
  649.                     cp != NULLCHAR ? cp : "");
  650.                 goto quit;
  651.             }
  652.         } else {
  653.             goto quit;
  654.         }
  655.     }
  656.     alarm(0L);
  657. #ifdef SMTPTRACE
  658.     if (Smtptrace)
  659.         printf("Connected\n");
  660. #endif
  661.     pwait (NULL);
  662.  
  663. #ifdef    LZW
  664.     rcode = getresp(cb,200);
  665.     if(rcode == -1 || rcode >= 400)
  666.         goto quit;
  667.  
  668.     if(Smtpclzw) {
  669.         char cp[LINELEN];
  670.         sendcmd(cb,"XLZW %d %d\n",Lzwbits,Lzwmode);
  671.         usflush(cb->s);
  672.         if(recvline(cb->s,cp,sizeof(cp)) == -1)
  673.             goto quit;
  674.         rip(cp);
  675. #ifdef    SMTPTRACE
  676.         if(Smtptrace)
  677.             printf(smtp_recv,cp);/* Display to user */
  678. #endif
  679.         rcode = lzwmode = lzwbits = 0;
  680.         sscanf(cp,"%d %d %d",&rcode,&lzwbits,&lzwmode);
  681.         if((rcode >= 200) && (rcode < 300)) {
  682.             smtpbatch = 1;
  683.             if(lzwmode != Lzwmode || lzwbits != Lzwbits) {
  684.                 lzwmode = LZWCOMPACT;
  685.                 lzwbits = LZWBITS;
  686.             }
  687.             lzwinit(cb->s,lzwbits,lzwmode);
  688.         } else {
  689.             smtpbatch = Smtpbatch;
  690.         }
  691.     } else {
  692.         smtpbatch = Smtpbatch;
  693.     }
  694. #else
  695.     smtpbatch = Smtpbatch;
  696.     if(!smtpbatch){
  697.         rcode = getresp(cb,200);
  698.         if(rcode == -1 || rcode >= 400)
  699.             goto quit;
  700.     }
  701. #endif
  702.     /* Say HELO */
  703.     sendcmd(cb,"HELO %s\n",Hostname);
  704.     if(!smtpbatch){
  705.         rcode = getresp(cb,200);
  706.         if(rcode == -1 || rcode >= 400)
  707.             goto quit;
  708.     }
  709.     do {    /* For each message... */
  710.  
  711.         pwait (NULL);
  712.         /* if this file open fails, skip it */
  713.         if ((cb->tfile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
  714.             continue;
  715.  
  716.         /* Send MAIL and RCPT commands */
  717.         sendcmd(cb,"MAIL FROM:<%s>\n",cb->jobq->from);
  718.         if(!smtpbatch){
  719.             rcode = getresp(cb,200);
  720.             if(rcode == -1 || rcode >= 400)
  721.                 goto quit;
  722.         }
  723.         rcpts = 0;
  724.         goodrcpt = 0;
  725.         for (tp = cb->jobq->to; tp != NULLLIST; tp = tp->next){
  726.             sendcmd(cb,"RCPT TO:<%s>\n",tp->val);
  727.             if(!smtpbatch){
  728.                 rcode = getresp(cb,200);
  729.                 if(rcode == -1)
  730.                     goto quit;
  731.                 if(rcode < 400)
  732.                     goodrcpt = 1; /* At least one good */
  733.             }
  734.             rcpts++;
  735.         }
  736.         /* Send DATA command */
  737.         sendcmd(cb,"DATA\n");
  738.         pwait (NULL);
  739.         if(!smtpbatch){
  740.             rcode = getresp(cb,200);
  741.             if(rcode == -1 || rcode >= 400)
  742.                 goto quit;
  743.         }
  744.         if(smtpbatch){
  745.             /* Now wait for the responses to come back. The first time
  746.              * we do this, we wait first for the start banner and
  747.              * HELO response. In any case, we wait for the response to
  748.              * the MAIL command here.
  749.              */
  750. #ifdef    LZW
  751.             for(i= init ? 2 : 1;i > 0;i--){
  752. #else
  753.             for(i= init ? 3 : 1;i > 0;i--){
  754. #endif
  755.                 rcode = getresp(cb,200);
  756.                 if(rcode == -1 || rcode >= 400)
  757.                     goto quit;
  758.             }
  759.             init = 0;
  760.  
  761.             /* Now process the responses to the RCPT commands */
  762.             for(i=rcpts;i!=0;i--){
  763.                 rcode = getresp(cb,200);
  764.                 if(rcode == -1)
  765.                     goto quit;
  766.                 if(rcode < 400)
  767.                     goodrcpt = 1; /* At least one good */
  768.             }
  769.             pwait (NULL);
  770.             /* And finally get the response to the DATA command.
  771.              * Some servers will return failure here if no recipients
  772.              * are valid, some won't.
  773.              */
  774.             rcode = getresp(cb,200);
  775.             if(rcode == -1 || rcode >= 400)
  776.                 goto quit;
  777.  
  778.             /* check for no good rcpt on the list */
  779.             if (goodrcpt == 0){
  780.                 sendcmd(cb,".\n");  /* Get out of data mode */
  781.                 goto quit;
  782.             }
  783.         }
  784.         /* Send the file. This also closes it */
  785.         smtpsendfile(cb);
  786.  
  787.         /* Wait for the OK response */
  788.         rcode = getresp(cb,200);
  789.         if(rcode == -1)
  790.             goto quit;
  791.         pwait (NULL);
  792.         if((rcode >= 200 && rcode < 300) || rcode >= 500){
  793.             /* if a good transfer or permanent failure remove job */
  794.  
  795.             if (cb->errlog != NULLLIST)
  796.                 retmail(cb);
  797.             /* Unlink the textfile */
  798.             (void) unlink(cb->tname);
  799.             (void) unlink(cb->wname);    /* unlink workfile */
  800.             log(cb->s,"SMTP sent job %s To: %s From: %s",
  801.              cb->jobq->jobname,cb->jobq->to->val,cb->jobq->from);
  802.         }
  803.     } while(next_job(cb));
  804. quit:
  805.     pwait (NULL);
  806.     sendcmd(cb,"QUIT\n");
  807.     if (cb->errlog != NULLLIST){
  808.         retmail(cb);
  809.         (void) unlink(cb->wname);    /* unlink workfile */
  810.         (void) unlink(cb->tname);    /* unlink text */
  811.     }
  812.     (void) close_s(cb->s);
  813.     if(cb->tfile != NULLFILE)
  814.         fclose(cb->tfile);
  815.     cb->lock = 0;
  816.     del_session(cb);
  817. }
  818.  
  819. /* free the message struct and data */
  820. static void
  821. del_session(cb)
  822. register struct smtpcli *cb;
  823. {
  824.     register struct smtp_job *jp,*tp;
  825.     register int i;
  826.  
  827.     if (cb == NULLSMTPCLI)
  828.         return;
  829.     for(i=0; i<MAXSESSIONS; i++) 
  830.         if(cli_session[i] == cb) {
  831.             cli_session[i] = NULLSMTPCLI;
  832.             break;
  833.         }
  834.  
  835.     free(cb->wname);
  836.     free(cb->tname);
  837.     free(cb->destname);
  838.     for (jp = cb->jobq; jp != NULLJOB;jp = tp) {
  839.             tp = jp->next;
  840.             del_job(jp);
  841.     }
  842.     del_list(cb->errlog);
  843.     free((char *)cb);
  844.     Smtpsessions--;    /* number of connections active */
  845. }
  846.  
  847. static void
  848. del_job(jp)
  849. register struct smtp_job *jp;
  850. {
  851.     if ( *jp->jobname != '\0')
  852.         (void) rmlock(Mailqdir,jp->jobname);
  853.     free(jp->from);
  854.     del_list(jp->to);
  855.     free((char *)jp);
  856. }
  857.  
  858. /* delete a list of list structs */
  859. void
  860. del_list(lp)
  861. struct list *lp;
  862. {
  863.     register struct list *tp, *tp1;
  864.     for (tp = lp; tp != NULLLIST; tp = tp1) {
  865.         tp1 = tp->next;
  866.         free(tp->val);
  867.         free(tp->orig);
  868.         free((char *)tp);
  869.     }
  870. }
  871.  
  872. /* stub for calling mdaemon to return message to sender */
  873. static void
  874. retmail(cb)
  875. struct smtpcli *cb;
  876. {
  877.     FILE *infile;
  878. #ifdef SMTPTRACE
  879.     if (Smtptrace > 5) {
  880.         printf("smtp job %s returned to sender\n",cb->wname);
  881.     }
  882. #endif
  883.     if ((infile = fopen(cb->tname,READ_TEXT)) == NULLFILE)
  884.         return;
  885. /*    mdaemon(infile,cb->jobq->from,cb->errlog,1);    */
  886.     mdaemon(infile,"sysop",cb->errlog,1);
  887.     fclose(infile);
  888. }
  889.  
  890. /* look to see if a smtp control block exists for this ipdest */
  891. static struct smtpcli *
  892. lookup(destaddr)
  893. int32 destaddr;
  894. {
  895.     register int i;
  896.  
  897.     for(i=0; i<MAXSESSIONS; i++) {
  898.         if (cli_session[i] == NULLSMTPCLI)
  899.             continue;
  900.         if(cli_session[i]->ipdest == destaddr)
  901.             return cli_session[i];
  902.     }
  903.     return NULLSMTPCLI;
  904. }
  905.  
  906. /* create a new  smtp control block */
  907. static struct smtpcli *
  908. newcb()
  909. {
  910.     register int i;
  911.     register struct smtpcli *cb;
  912.  
  913.     for(i=0; i<MAXSESSIONS; i++) {
  914.         if(cli_session[i] == NULLSMTPCLI) {
  915.             cb = (struct smtpcli *)callocw(1,sizeof(struct smtpcli));
  916.             cb->wname = mallocw((unsigned)strlen(Mailqdir)+JOBNAME);
  917.             cb->tname = mallocw((unsigned)strlen(Mailqdir)+JOBNAME);
  918.             cli_session[i] = cb;
  919.             Smtpsessions++;    /* number of connections active */
  920.             return(cb);
  921.         }
  922.     }
  923.     return NULLSMTPCLI;
  924. }
  925.  
  926. static void
  927. execjobs()
  928. {
  929.     register struct smtpcli *cb;
  930.     register int i, insave, outsave;
  931.  
  932.     for(i=0; i<MAXSESSIONS; i++) {
  933.         pwait (NULL);
  934.         cb = cli_session[i];
  935.         if (cb == NULLSMTPCLI) 
  936.             continue;
  937.         if(cb->lock)
  938.             continue;
  939.  
  940.         sprintf(cb->tname,"%s/%s.txt",Mailqdir,cb->jobq->jobname);
  941.         sprintf(cb->wname,"%s/%s.wrk",Mailqdir,cb->jobq->jobname);
  942.  
  943.         /* This solves the nasty hack in mailbox.c, from Mark ve3dte */
  944.         insave = Curproc->input;
  945.         outsave = Curproc->output;
  946.         Curproc->input = -1;
  947.         Curproc->output = -1;
  948.         /* Now we can call newproc with null parent sockets! */
  949.         newproc("smtp_send", 1024, smtp_send, 0, cb,NULL,0);
  950.         /* now restore parent sockets so parent can continue */
  951.         Curproc->input = insave;
  952.         Curproc->output = outsave;
  953.  
  954. #ifdef SMTPTRACE
  955.         if (Smtptrace) 
  956.             printf("Trying Connection to %s\n",inet_ntoa(cb->ipdest));
  957. #endif
  958.  
  959.  
  960.     }
  961. }
  962.     
  963. /* add this job to control block queue */
  964. static struct smtp_job *
  965. setupjob(cb,id,from)
  966. struct smtpcli *cb;
  967. char *id,*from;
  968. {
  969.     register struct smtp_job *p1,*p2;
  970.  
  971.     p1 = (struct smtp_job *)callocw(1,sizeof(struct smtp_job));
  972.     p1->from = strdup(from);
  973.     strcpy(p1->jobname,id);
  974.     /* now add to end of jobq */
  975.     if ((p2 = cb->jobq) == NULLJOB)
  976.         cb->jobq = p1;
  977.     else {
  978.         while(p2->next != NULLJOB)
  979.             p2 = p2->next;
  980.         p2->next = p1;
  981.     }
  982.     return p1;
  983. }
  984.  
  985. /* called to advance to the next job */
  986. static int
  987. next_job(cb)
  988. register struct smtpcli *cb;
  989. {
  990.     register struct smtp_job *jp;
  991.  
  992.     jp = cb->jobq->next;
  993.     del_job(cb->jobq);
  994.     /* remove the error log of previous message */
  995.     del_list(cb->errlog);
  996.     cb->errlog = NULLLIST;
  997.     cb->jobq = jp;
  998.     if (jp == NULLJOB)
  999.         return 0;
  1000.     sprintf(cb->tname,"%s/%s.txt",Mailqdir,jp->jobname);
  1001.     sprintf(cb->wname,"%s/%s.wrk",Mailqdir,jp->jobname);
  1002. #ifdef SMTPTRACE
  1003.     if (Smtptrace > 5) {
  1004.         printf("sending job %s\n",jp->jobname);
  1005.     }
  1006. #endif
  1007.         return 1;
  1008.  
  1009. }
  1010.  
  1011.  
  1012. /* Mail routing function. For now just use the hosts file */
  1013. int32
  1014. mailroute(dest)
  1015. char *dest;
  1016. {
  1017. int32 destaddr = 0L;
  1018.  
  1019. #ifdef SMTPTRACE
  1020.     if (Smtptrace > 6)
  1021.         printf("MX lookup for = %s\n",dest);
  1022. #endif
  1023.     /* look up address or use the gateway */
  1024.     if(UseMX){
  1025.         destaddr = resolve_mx(dest);
  1026. #ifdef SMTPTRACE
  1027.         if (Smtptrace > 6)
  1028.             printf("MX lookup returned = %s\n",inet_ntoa(destaddr));
  1029. #endif
  1030.     }
  1031.     pwait (NULL);
  1032.     if(destaddr == 0L)
  1033.         if((destaddr = resolve(dest)) == 0L)
  1034.             if (Gateway != 0) 
  1035.                 destaddr = Gateway; /* Use the gateway  */
  1036. #ifdef SMTPTRACE
  1037.     if (Smtptrace > 6)
  1038.         printf("Mailroute returned = %s\n",inet_ntoa(destaddr));
  1039. #endif
  1040.     return destaddr;
  1041.     
  1042. }
  1043.  
  1044. /* save line in error list */
  1045. static void
  1046. logerr(cb,line)
  1047. struct smtpcli *cb;
  1048. char *line;
  1049. {
  1050.     register struct list *lp,*tp;
  1051.     tp = (struct list *)callocw(1,sizeof(struct list));
  1052.     tp->val = strdup(line);
  1053.     /* find end of list */
  1054.     if ((lp = cb->errlog) == NULLLIST)
  1055.         cb->errlog = tp;
  1056.     else {
  1057.         while(lp->next != NULLLIST)
  1058.             lp = lp->next;
  1059.         lp->next = tp;
  1060.     }
  1061. }
  1062.  
  1063. static int
  1064. smtpsendfile(cb)
  1065. register struct smtpcli *cb;
  1066. {
  1067.     int error = 0;
  1068.  
  1069.     strcpy(cb->buf,"\n");
  1070.     while(fgets(cb->buf,sizeof(cb->buf),cb->tfile) != NULLCHAR) {
  1071.         /* Escape a single '.' character at the beginning of a line */
  1072.         if(strcmp(cb->buf,".\n") == 0)
  1073.             usputc(cb->s,'.');
  1074.         usputs(cb->s,cb->buf);
  1075.         pwait (NULL);
  1076.     }
  1077.     fclose(cb->tfile);
  1078.     cb->tfile = NULLFILE;
  1079.     /* Send the end-of-message command */
  1080.     if(cb->buf[strlen(cb->buf)-1] == '\n')
  1081.         sendcmd(cb,".\n");
  1082.     else
  1083.         sendcmd(cb,"\n.\n");
  1084.     return error;
  1085. }
  1086. /* do a printf() on the socket with optional local tracing */
  1087. #ifdef    ANSIPROTO
  1088. static void
  1089. sendcmd(struct smtpcli *cb,char *fmt, ...)
  1090. {
  1091.     va_list args;
  1092.  
  1093.     va_start(args,fmt);
  1094. #ifdef    SMTPTRACE
  1095.     if(Smtptrace){
  1096.         printf("smtp sent: ");
  1097.         vprintf(fmt,args);
  1098.     }
  1099. #endif
  1100.     vsprintf(cb->buf,fmt,args);
  1101.     usputs(cb->s,cb->buf);
  1102.     va_end(args);
  1103. }
  1104. #else
  1105. static void
  1106. sendcmd(cb,fmt,arg1,arg2,arg3,arg4)
  1107. struct smtpcli *cb;
  1108. char *fmt;
  1109. int arg1,arg2,arg3,arg4;
  1110. {
  1111. #ifdef    SMTPTRACE
  1112.     if(Smtptrace){
  1113.         printf("smtp sent: ");
  1114.         printf(fmt,arg1,arg2,arg3,arg4);
  1115.     }
  1116. #endif
  1117.     sprintf(cb->buf,fmt,arg1,arg2,arg3,arg4);
  1118.     usputs(cb->s,cb->buf);
  1119. }
  1120. #endif
  1121.  
  1122. /* Wait for, read and display response from server. Return the result code. */
  1123. static int
  1124. getresp(cb,mincode)
  1125. struct smtpcli *cb;
  1126. int mincode;    /* Keep reading until at least this code comes back */
  1127. {
  1128.     int rval;
  1129.     char line[LINELEN];
  1130.  
  1131.     usflush(cb->s);
  1132.     for(;;){
  1133.         pwait (NULL);
  1134.         /* Get line */
  1135.         if(recvline(cb->s,line,LINELEN) == -1){
  1136.             rval = -1;
  1137.             break;
  1138.         }
  1139.         rip(line);        /* Remove cr/lf */
  1140.         rval = atoi(line);
  1141. #ifdef    SMTPTRACE
  1142.         if(Smtptrace)
  1143.             printf(smtp_recv,line);/* Display to user */
  1144. #endif
  1145.         if(rval >= 500) {    /* Save permanent error replies */
  1146.             char tmp[LINELEN];
  1147.             if(cb->errlog == NULLLIST) {
  1148.                 sprintf(tmp,"While talking to %s:",
  1149.                     cb->destname);
  1150.                 logerr(cb,tmp);
  1151.             }
  1152.             if(cb->buf[0] != '\0') { /* Save offending command */
  1153.                 rip(cb->buf);
  1154.                 sprintf(tmp,">>> %s",cb->buf);
  1155.                 logerr(cb,tmp);
  1156.                 cb->buf[0] = '\0';
  1157.             }
  1158.             sprintf(tmp,"<<< %s",line);
  1159.             logerr(cb,tmp);        /* save the error reply */
  1160.         }
  1161.         /* Messages with dashes are continued */
  1162.         if(line[3] != '-' && rval >= mincode)
  1163.             break;
  1164.     }
  1165.     return rval;
  1166. }
  1167.